﻿using SharpDepend.Datatypes;
using SoundEngine3.DspPlugins;
using System.Collections.Generic;
using ZquenceStudio3.Core;
using ZquenceStudio3.DspPlugins;
using ZquenceStudio3Host.Graphics.Components;
using static ZquenceStudio3Host.DspPlugins.Synthesizers.Layer;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers
{
    public class LayerUIDescription : IDspUIPlugin
    {
        public IDspUIPluginInfo GetDspUIPluginInfo()
        {
            return new DspUIPluginInfo<Layer, LayerUI>();
        }

        public BaseDspUIPluginAdapter CreateDspUI(ZquenceStudio3Context context, BaseDspPluginInstance dsp)
        {
            return new LayerUI();
        }
    }

    public class LayerUI : GuiDspPlugin
    {
        private Layer mLayer;
        private Listbox availableSynthesizers;
        private Dictionary<SoundEngine3.SequenceContent.Track, ListboxItem> mItems = new Dictionary<SoundEngine3.SequenceContent.Track, ListboxItem>();
        private SoundEngine3.SequenceContent.Track mSelectedItem;
        private Textbox noteStart;
        private Textbox noteEnd;
        private Textbox ocaveOffset;
        private Button buttonMap;
        
        protected override void OnSetDspPlugin(BaseDspPluginInstance dsp)
        {
            mLayer = (Layer)dsp;
            mLayer.OnParsedData += Layer_OnParsedData;

            mLayer.CoreContext.SoundEngine.Sequencer.OnTrackAdded += ZSController_OnTrackAdded;
            mLayer.CoreContext.SoundEngine.Sequencer.OnTrackRemoved += ZSController_OnTrackRemoved;
        }

        private void Layer_OnParsedData()
        {
            // Select first synthesizer if available.
            SelectFirstAndUpdate();
        }

        public override void Dispose()
        {
            mLayer.CoreContext.SoundEngine.Sequencer.OnTrackAdded -= ZSController_OnTrackAdded;
            mLayer.CoreContext.SoundEngine.Sequencer.OnTrackRemoved -= ZSController_OnTrackRemoved;

            base.Dispose();
        }

        public override bool EditorGetRect(out SharpDepend.Datatypes.Vector4 wndRect)
        {
            wndRect = new SharpDepend.Datatypes.Vector4(0, 0, 500, 300);

            return true;
        }

        public override bool EditorResizeable
        {
            get
            {
                return false;
            }
        }

        protected override void InitializeGuiComponents(Panel gui)
        {

            Color componentBackColor = new Color(40, 40, 40);

            gui.BackgroundColor = new Color(65, 65, 65);

            const int TextSize = 7;

            // Listbox with all synthesizers.
            availableSynthesizers = new Listbox(Gui);
            availableSynthesizers.DimensionInProcent = true;
            availableSynthesizers.Width = 60;
            availableSynthesizers.Height = 100;
            availableSynthesizers.BackgroundColor = componentBackColor;
            availableSynthesizers.OnSelectedListboxItem += AvailableSynthesizers_OnSelectedListboxItem;
            gui.AddComponent(availableSynthesizers);

            Panel right = new Panel(Gui);
            right.BackgroundColor = Color.Transparent;
            right.DimensionInProcent = true;
            right.X = 60;
            right.Width = 40;
            right.Height = 100;
            gui.AddComponent(right);

            // Note start spinner.
            Label lbl1 = new Label(Gui);
            lbl1.DimensionInProcent = true;
            lbl1.Text = "Note start:";
            lbl1.Width = 100;
            lbl1.Height = TextSize;
            lbl1.Y = 0;
            lbl1.AutoSize = true;
            right.AddComponent(lbl1);
            noteStart = new Textbox(Gui);
            noteStart.DimensionInProcent = true;
            noteStart.Width = 100;
            noteStart.Height = 10;
            noteStart.Text = "0";
            noteStart.OnTextChanged += NoteStart_OnTextChanged;
            noteStart.BackgroundColor = componentBackColor;
            NextY(lbl1, noteStart);
            right.AddComponent(noteStart);

            // Note end spinner.
            lbl1 = new Label(Gui);
            lbl1.DimensionInProcent = true;
            lbl1.Text = "Note end:";
            lbl1.Width = 100;
            lbl1.Height = TextSize;
            lbl1.AutoSize = true;
            NextY(noteStart, lbl1);
            right.AddComponent(lbl1);
            noteEnd = new Textbox(Gui);
            noteEnd.DimensionInProcent = true;
            noteEnd.Width = 100;
            noteEnd.Height = 10;
            noteEnd.Text = "120";
            noteEnd.OnTextChanged += NoteEnd_OnTextChanged;
            noteEnd.BackgroundColor = componentBackColor;
            NextY(lbl1, noteEnd);
            right.AddComponent(noteEnd);

            // Octave offset spinner.
            lbl1 = new Label(Gui);
            lbl1.DimensionInProcent = true;
            lbl1.Text = "Octave offset:";
            lbl1.Width = 100;
            lbl1.Height = TextSize;
            lbl1.AutoSize = true;
            NextY(noteEnd, lbl1);
            right.AddComponent(lbl1);
            ocaveOffset = new Textbox(Gui);
            ocaveOffset.DimensionInProcent = true;
            ocaveOffset.Width = 100;
            ocaveOffset.Height = 10;
            ocaveOffset.Text = "0";
            ocaveOffset.OnTextChanged += OcaveOffset_OnTextChanged;
            ocaveOffset.BackgroundColor = componentBackColor;
            NextY(lbl1, ocaveOffset);
            right.AddComponent(ocaveOffset);

            // Map button, connect selected synthesizer to keyboard.
            buttonMap = new Button(Gui);
            buttonMap.SetButtonColor(componentBackColor);
            buttonMap.DimensionInProcent = true;
            buttonMap.Width = 100;
            buttonMap.Height = 10;
            buttonMap.OnClick += Map_OnClick;
            buttonMap.Label.Text = "Map";
            NextY(ocaveOffset, buttonMap);
            right.AddComponent(buttonMap);

            // Load all synthesizer to list.
            foreach (var track in Application.Application.Core.SoundEngine.Sequencer.Tracks)
            {
                ZSController_OnTrackAdded(track);
            }

            // Select first synthesizer if available.
            SelectFirstAndUpdate();
        }
        
        void NextY(Panel previous, Panel next)
        {
            if (previous != null)
            {
                next.Y = previous.Y + previous.Height + 2;
            }
        }

        private void OcaveOffset_OnTextChanged(string text)
        {
            if (mSelectedItem != null)
            {
                if (mLayer.LayerMaps.ContainsKey(mSelectedItem))
                {
                    int number;
                    if (int.TryParse(text, out number))
                    {
                        mLayer.LayerMaps[mSelectedItem].OctaveOffset = number;
                        ocaveOffset.IsValid = true;
                    }
                    else
                    {
                        ocaveOffset.IsValid = false;
                    }
                }
            }
        }

        private void NoteStart_OnTextChanged(string text)
        {
            if (mSelectedItem != null)
            {
                if (mLayer.LayerMaps.ContainsKey(mSelectedItem))
                {
                    int number;
                    if(int.TryParse(text, out number))
                    {
                        mLayer.LayerMaps[mSelectedItem].NoteStart = number;
                        noteStart.IsValid = true;
                    }
                    else
                    {
                        noteStart.IsValid = false;
                    }
                }
            }
        }

        private void NoteEnd_OnTextChanged(string text)
        {
            if (mSelectedItem != null)
            {
                if (mLayer.LayerMaps.ContainsKey(mSelectedItem))
                {
                    int number;
                    if (int.TryParse(text, out number))
                    {
                        mLayer.LayerMaps[mSelectedItem].NoteEnd = number;
                        noteEnd.IsValid = true;
                    }
                    else
                    {
                        noteEnd.IsValid = false;
                    }
                }
            }
        }

        private void ZSController_OnTrackAdded(SoundEngine3.SequenceContent.Track obj)
        {
            if (Gui != null && obj.DspSynthesizer != mLayer)
            {
                ListboxItem item = new ListboxItem(Gui, obj.ToString(), obj);
                item.ItemPanel.Height = 23;

                mItems.Add(obj, item);

                availableSynthesizers.AddItem(item);
            }
        }

        private void ZSController_OnTrackRemoved(SoundEngine3.SequenceContent.Track obj)
        {
            if (Gui != null && obj.DspSynthesizer != mLayer)
            {
                ListboxItem item = mItems[obj];
                mItems.Remove(obj);

                availableSynthesizers.RemoveItem(item);
            }
        }

        private void AvailableSynthesizers_OnSelectedListboxItem(object sender, ListboxItem selectedItem)
        {
            mSelectedItem = (SoundEngine3.SequenceContent.Track)selectedItem.ItemObject;

            if(!UpdateSelected())
            {
                // If no item was selected, then set textbox to default values.
                noteStart.Text = "0";
                noteEnd.Text = "120";
                ocaveOffset.Text = "0";
            }
        }

        private bool UpdateSelected()
        {
            // If map already exist, update UI.
            if (mLayer.LayerMaps.ContainsKey(mSelectedItem))
            {
                var map = mLayer.LayerMaps[mSelectedItem];
                noteStart.Text = map.NoteStart.ToString();
                noteEnd.Text = map.NoteEnd.ToString();
                ocaveOffset.Text = map.OctaveOffset.ToString();

                buttonMap.Label.Text = "Unmap";
                return true;
            }
            else
            {
                buttonMap.Label.Text = "Map";
                return false;
            }
        }

        private void Map_OnClick(object sender, double x, double y, int id)
        {
            if (mSelectedItem != null)
            {
                if (mLayer.LayerMaps.ContainsKey(mSelectedItem))
                {
                    mLayer.LayerMaps.Remove(mSelectedItem);
                    buttonMap.Label.Text = "Map";

                    ListboxItem item = mItems[mSelectedItem];
                    UnselectListItem(item.ItemPanel);
                }
                else
                {
                    if (!noteStart.IsValid)
                    {
                        SharpDepend.Manager.DialogManager.Show("Note start has not valid input.", "Map layer", SharpDepend.DialogIcon.Warning);
                    }
                    else if (!noteEnd.IsValid)
                    {
                        SharpDepend.Manager.DialogManager.Show("Note end has not valid input.", "Map layer", SharpDepend.DialogIcon.Warning);
                    }
                    else if (!ocaveOffset.IsValid)
                    {
                        SharpDepend.Manager.DialogManager.Show("Octave offset has not valid input.", "Map layer", SharpDepend.DialogIcon.Warning);
                    }
                    else
                    {
                        LayerMap layerMap = new LayerMap();
                        layerMap.Synthesizer = mSelectedItem;
                        layerMap.NoteStart = int.Parse(noteStart.Text);
                        layerMap.NoteEnd = int.Parse(noteEnd.Text);
                        layerMap.OctaveOffset = int.Parse(ocaveOffset.Text);

                        mLayer.LayerMaps.Add(mSelectedItem, layerMap);

                        // Colorize the synthesizer item.
                        ListboxItem item = mItems[mSelectedItem];
                        SelectListItem(item.ItemPanel);

                        buttonMap.Label.Text = "Unmap";
                    }
                }
            }
            else
            {
                SharpDepend.Manager.DialogManager.Show("You must select a synthesizer to map.", "Map layer", SharpDepend.DialogIcon.Warning);
            }
        }

        private static void SelectListItem(Panel panel)
        {
            Color color = Color.Orange;
            color.A = 90;
            panel.BackgroundColor = color;
        }

        private static void UnselectListItem(Panel panel)
        {
            panel.BackgroundColor = Color.Transparent;
        }
        
        private void SelectFirstAndUpdate()
        {
            if (availableSynthesizers != null)
            {
                if (availableSynthesizers.Count > 0)
                {
                    foreach (var item in availableSynthesizers.Items)
                    {
                        foreach (var layer in mLayer.LayerMaps)
                        {
                            if (item.ItemObject == layer.Key)
                            {
                                SelectListItem(item.ItemPanel);
                            }
                        }
                    }

                    availableSynthesizers.SelectIndex(0);

                    UpdateSelected();
                }
            }
        }
    }
}
